/**
 * 系统项目名称
 * com.yunmai.scale.db.manager
 * DBFactory.java
 * <p>
 * 2015-3-31-上午9:58:18
 * 2015云麦公司-版权所有
 */
package com.yunmai.library.db;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.Dao.CreateOrUpdateStatus;
import com.j256.ormlite.stmt.DeleteBuilder;
import com.j256.ormlite.stmt.QueryBuilder;
import com.j256.ormlite.stmt.StatementBuilder;
import com.j256.ormlite.stmt.UpdateBuilder;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 *
 * DBFactory
 *
 * YUNMAI
 *
 * 2015-3-31 上午9:58:18
 *
 *
 */
public class DBFactory {

    private static final int ACTION_UPDATE = 1;
    private static final int ACTION_CREATE = 2;
    private static final int ACTION_DELETE = 3;
    private static final int ACTION_CREATEORUPDATE = 4;
    private static final int ACTION_QUERYALL = 5;
    private static final int ACTION_QUERYONE = 6;
    private static final int ACTION_QUERYLAST = 7;
    //批量刷新插入数据
    private static final int ACTION_CREATE_OR_UPDATE_ALL = 8;
    private static final int HANDLER_RESULT = 1;
    private static final ArrayList<DBRequest<?>> dbRequests = new ArrayList<DBRequest<?>>();
    private static final Lock mLock = new ReentrantLock();
    private static final Condition mCondition = mLock.newCondition();
    private static Thread mThread;
    private static boolean isRun = false;
    private static DBFactory instance;
    public Context mContext;

    private Handler mHandler = new Handler(Looper.getMainLooper());

    private DBFactory(Context context) {
        this.mContext = context.getApplicationContext();
    }

    public static DBFactory getInstance(Context context) {
        if (instance == null)
            instance = new DBFactory(context);
        return instance;
    }

    private <T> void onChange(int action, T t) {
        List<AbstractDBChange<?>> changeList = getHelper().getChangeListenerList();
        for (AbstractDBChange<?> abstractDBChange : changeList) {
            if (abstractDBChange.getEntityName().equals(t.getClass().getName())) {
                AbstractDBChange<T> onAbstractDBChange = (AbstractDBChange<T>) abstractDBChange;
                switch (action) {
                    case ACTION_UPDATE:
                        onAbstractDBChange.onUpdate(t);
                        break;
                    case ACTION_CREATE:
                        onAbstractDBChange.onCreate(t);
                        break;
                    case ACTION_DELETE:
                        onAbstractDBChange.onDelete(t);
                        break;

                    default:
                        break;
                }
            }
        }

    }

    private <T> void onChange(int action, List<T> lists, Class<T> clazz) {
        List<AbstractDBChange<?>> changeList = getHelper().getChangeListenerList();
        synchronized (changeList) {
            for (AbstractDBChange<?> abstractDBChange : changeList) {
                if (abstractDBChange.getEntityName().equals(clazz.getName())) {
                    AbstractDBChange<T> onAbstractDBChange = (AbstractDBChange<T>) abstractDBChange;
                    switch (action) {
                        case ACTION_UPDATE:
                            onAbstractDBChange.onUpdate(lists);
                            break;
                        case ACTION_CREATE:
                            onAbstractDBChange.onCreate(lists);
                            break;
                        case ACTION_DELETE:
                            onAbstractDBChange.onDelete();
                            break;
                        case ACTION_CREATE_OR_UPDATE_ALL:
                            onAbstractDBChange.onCreateOrUpdate(lists);
                        default:
                            break;
                    }
                }
            }
        }
    }

    public <T> Dao<T, ?> getDao(Class<?> t) throws SQLException {
        return (Dao<T, ?>) getHelper().getDao(t);
    }

    /**
     * 获取数据库 对象
     * */
    public DBOpenHelper getHelper() {
        return DBOpenHelper.getInstance(mContext);
    }

    /**
     * 更新数据
     *
     * @param t
     * @return
     *int
     */
    public <T> int update(T t, boolean needListenChange) {
        try {
            Dao<T, ?> dao = getDao(t.getClass());
            int result = dao.update(t);
            if (result > 0 && needListenChange)
                onChange(ACTION_UPDATE, t);
            return result;
        } catch (SQLException e) {
        }
        return -1;
    }

    /**
     * 更新数据
     *
     * @param t
     * @return
     *int
     */
    public <T> int updateByBuilder(UpdateBuilder builder, Class t, boolean needListenChange) {
        try {
            Dao<T, ?> dao = getDao(t);
            int result = dao.update(builder.prepare());
            if (result > 0 && needListenChange)
                onChange(ACTION_UPDATE, t);
            return result;
        } catch (SQLException e) {
        }
        return -1;
    }

    /**
     * 批量更新数据
     *
     * @param t
     *void
     */
    public <T> void update(final List<T> t, Class<T> clazz, boolean needListenChange) {
        if (t == null || t.size() == 0)
            return;
        final Dao<T, ?> dao;
        try {
            dao = getDao(clazz);
            dao.callBatchTasks(new Callable<Void>() {

                @Override
                public Void call() throws Exception {
                    for (T t2 : t) {
                        dao.update(t2);
                    }
                    return null;
                }
            });
            if (needListenChange)
                onChange(ACTION_UPDATE, t, clazz);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建一条数据
     *
     * @param t
     * @return
     *int
     */
    public <T> int create(T t, boolean needListenChange) {
        try {
            Dao<T, ?> dao = getDao(t.getClass());
            int result = dao.create(t);
            if (result > 0 && needListenChange)
                onChange(ACTION_CREATE, t);
            return result;
        } catch (SQLException e) {
        }
        return -1;
    }

    /**
     * 批量创建数据
     *
     * @param t
     * @return
     *int
     */
    public <T> void create(final List<T> t, Class<T> clazz, boolean needListenChange) {
        try {
            final Dao<T, ?> dao = getDao(clazz);
            dao.callBatchTasks(new Callable<Void>() {

                @Override
                public Void call() throws Exception {
                    for (T t2 : t) {
                        dao.create(t2);
                    }
                    return null;
                }
            });
            if (needListenChange)
                onChange(ACTION_CREATE, t, clazz);
        } catch (Exception e) {
        }
    }

    /**
     * 创建或者更新一条数据
     *
     * @param t
     * @return
     *int
     */
    public <T> int createOrUpdate(T t, boolean needListenChange) {
        try {
            Dao<T, ?> dao = getDao(t.getClass());
            CreateOrUpdateStatus status = dao.createOrUpdate(t);
            if (needListenChange) {
                if (status.isCreated()) {
                    onChange(ACTION_CREATE, t);
                } else if (status.isUpdated()) {
                    onChange(ACTION_UPDATE, t);
                }
            }
            return status.getNumLinesChanged();
        } catch (SQLException e) {
        }
        return -1;
    }

    /**
     * 创建或者更新一条数据
     *
     * @param t
     * @return
     *int
     */
    public <T> void createOrUpdate(List<T> t, Class<T> clazz, boolean needListenChange) {
        try {
            Dao<T, ?> dao = getDao(clazz);
            for (T t2 : t) {
                CreateOrUpdateStatus status = dao.createOrUpdate(t2);
                if (needListenChange) {
                    if (status.isCreated()) {
                        onChange(ACTION_CREATE, t2);
                    } else if (status.isUpdated()) {
                        onChange(ACTION_UPDATE, t2);
                    }
                }
            }
            onChange(ACTION_CREATE_OR_UPDATE_ALL, t, clazz);
        } catch (SQLException e) {
        }
    }

    /**
     * 删除一条数据
     *
     * @param t
     * @return
     *int
     */
    public <T> int delete(T t, StatementBuilder<T, ?> q, boolean needListenChange) {
        try {
            Dao<T, ?> dao = getDao(t.getClass());
            if (needListenChange)
                onChange(ACTION_DELETE, t);
            if (q != null) {
                DeleteBuilder<T, ?> deleteBuilder = (DeleteBuilder<T, ?>) q;
                return dao.delete(deleteBuilder.prepare());
            }
            return dao.delete(t);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return -1;
    }

    public <T> int deleteById(Class<T> clazz, long id, boolean needListenChange) {
        try {
            Dao<T, Long> dao = (Dao<T, Long>) getDao(clazz);
            if (needListenChange) {
                onChange(ACTION_DELETE, id);
            }

            return dao.deleteById(id);
        } catch (SQLException e) {
        }
        return -1;

    }


    /**
     * 批量更新数据
     *
     * @param t
     *void
     */
    public <T> void delete(List<T> t, Class<T> clazz, boolean needListenChange) {
        if (t == null || t.size() == 0)
            return;
        Dao<T, ?> dao;
        try {
            dao = getDao(clazz);
            for (T t2 : t) {
                dao.delete(t2);
            }
            if (needListenChange)
                onChange(ACTION_UPDATE, t, clazz);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


    /**
     * 删除数据
     *
     * @param clazz
     * @return
     *int
     */
    public <T> int delete(Class<T> clazz, StatementBuilder<T, ?> q, boolean needListenChange) {
        try {
            Dao<T, ?> dao = getDao(clazz);
            if (q != null) {
                if (needListenChange)
                    onChange(ACTION_DELETE, null, clazz);
                DeleteBuilder<T, ?> deleteBuilder = (DeleteBuilder<T, ?>) q;
                return dao.delete(deleteBuilder.prepare());
            }
        } catch (SQLException e) {
        }
        return -1;
    }

    /**
     * 查询符合条件的全部数据
     *
     * @param t
     * @return
     *List<T>
     */
    public <T> List<T> query(Class<T> t, StatementBuilder<Object, ?> q) {
        try {
            Dao<T, ?> dao = getDao(t);
            if (q == null) {
                return dao.queryForAll();
            }
            QueryBuilder<T, ?> queryBuilder = (QueryBuilder<T, ?>) q;
            return dao.query(queryBuilder.prepare());
        } catch (SQLException e) {
        }
        return null;
    }

    /**
     * 查询第一条数据
     *
     * @param t
     * @return
     *T
     */
    public <T> T queryOne(Class<T> t, StatementBuilder<Object, ?> q) {
        try {
            Dao<T, ?> dao = getDao(t);
            if (q == null) {
                List<T> lists = dao.queryForAll();
                if (lists != null && lists.size() > 0) {
                    return lists.get(lists.size() - 1);
                }
            }
            QueryBuilder<T, ?> queryBuilder = (QueryBuilder<T, ?>) q;
            return dao.queryForFirst(queryBuilder.prepare());
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * 查询最后一条数据
     *
     * @param t
     * @return
     *T
     */
    public <T> T queryLast(Class<T> t, StatementBuilder<Object, ?> q) {
        List<T> lists;
        lists = query(t, q);
        if (lists != null && lists.size() > 0) {
            return lists.get(lists.size() - 1);
        }
        return null;
    }

    /**
     * 查询是否存在
     *
     * @param t
     * @return
     *boolean
     */
    public <T> long getCount(Class<T> t, StatementBuilder<Object, ?> q) {
        Dao<T, ?> dao;
        try {
            dao = getDao(t);
            if (q != null) {
                QueryBuilder<T, ?> queryBuilder = (QueryBuilder<T, ?>) q;
                queryBuilder.setCountOf(true);
                return dao.countOf(queryBuilder.prepare());
            }
        } catch (SQLException e) {
        }
        return 0;
    }

    /**
     * 异步创建或更新数据
     *
     * @param t
     * @param idbManagerListener
     *void
     */
    public <T> void asyncCreateOrUpdate(T t, IDBManagerListener idbManagerListener,
                                        StatementBuilder<Object, ?> q, boolean needListenChange) {
        DBRequest<T> dbRequest = new DBRequest<T>();
        dbRequest.addObj(t);
        dbRequest.setNeedListenChange(needListenChange);
        dbRequest.setQueryBuilder(q);
        dbRequest.setClazz((Class<T>) t.getClass());
        dbRequest.setiListener(idbManagerListener);
        dbRequest.setAction(ACTION_CREATEORUPDATE);
        addDBRequest(dbRequest);
    }

    /**
     * 异步创建或更新数据
     *
     * @param idbManagerListener
     *void
     */
    public <T> void asyncCreateOrUpdate(List<T> lists, IDBManagerListener idbManagerListener,
                                        Class<T> clazz, StatementBuilder<Object, ?> queryBuilder,
                                        boolean needListenChange) {
        DBRequest<T> dbRequest = new DBRequest<T>();
        dbRequest.setObj(lists);
        dbRequest.setiListener(idbManagerListener);
        dbRequest.setNeedListenChange(needListenChange);
        dbRequest.setQueryBuilder(queryBuilder);
        dbRequest.setClazz(clazz);
        dbRequest.setAction(ACTION_CREATEORUPDATE);
        addDBRequest(dbRequest);
    }

    /**
     * 异步查询符合条件的全部数据
     *
     * @param idbManagerListener
     *void
     */
    public <T> void asyncQueryAll(Class<T> clazz, IDBManagerListener idbManagerListener,
                                  StatementBuilder<Object, ?> queryBuilder) {
        DBRequest<T> dbRequest = new DBRequest<T>();
        dbRequest.setClazz(clazz);
        dbRequest.setiListener(idbManagerListener);
        dbRequest.setQueryBuilder(queryBuilder);
        dbRequest.setAction(ACTION_QUERYALL);
        addDBRequest(dbRequest);
    }

    /**
     * 异步查询第一条数据
     *
     * @param idbManagerListener
     *void
     */
    public <T> void asyncQueryOne(Class<T> clazz, IDBManagerListener idbManagerListener,
                                  StatementBuilder<Object, ?> queryBuilder) {
        DBRequest<T> dbRequest = new DBRequest<T>();
        dbRequest.setClazz(clazz);
        dbRequest.setQueryBuilder(queryBuilder);
        dbRequest.setiListener(idbManagerListener);
        dbRequest.setAction(ACTION_QUERYONE);
        addDBRequest(dbRequest);
    }

    /**
     * 异步查询最后一条数据
     *
     * @param idbManagerListener
     *void
     */
    public <T> void asyncQueryLast(Class<T> clazz, IDBManagerListener idbManagerListener,
                                   StatementBuilder<Object, ?> queryBuilder) {
        DBRequest<T> dbRequest = new DBRequest<T>();
        dbRequest.setClazz(clazz);
        dbRequest.setQueryBuilder(queryBuilder);
        dbRequest.setiListener(idbManagerListener);
        dbRequest.setAction(ACTION_QUERYLAST);
        addDBRequest(dbRequest);
    }

    private <T> void addDBRequest(DBRequest<T> dbRequest) {
        mLock.lock();
        dbRequests.add(dbRequest);
        if (!isRun || mThread == null) {
            isRun = true;
            mThread = new Thread(new MRunable<T>(), "DBThread");
            mThread.start();
        }
        mCondition.signal();
        mLock.unlock();
    }

    private <T> void doAction(DBRequest<T> mDbRequest) {
        Object result = null;
        switch (mDbRequest.getAction()) {
            case ACTION_CREATE:
                if (mDbRequest.getOne() != null) {
                    result = create(mDbRequest.getOne(), mDbRequest.isNeedListenChange());
                }
                break;
            case ACTION_DELETE:
                if (mDbRequest.getOne() != null) {
                    result = delete(mDbRequest.getOne(), mDbRequest.getQueryBuilder(),
                        mDbRequest.isNeedListenChange());
                }
                break;
            case ACTION_CREATEORUPDATE:
                createOrUpdate(mDbRequest.getObj(), mDbRequest.getClazz(),
                    mDbRequest.isNeedListenChange());
                break;
            case ACTION_QUERYALL:
                result = query(mDbRequest.getClazz(), mDbRequest.getQueryBuilder());
                break;
            case ACTION_QUERYONE:
                result = queryOne(mDbRequest.getClazz(), mDbRequest.getQueryBuilder());
                break;
            case ACTION_QUERYLAST:
                result = queryLast(mDbRequest.getClazz(), mDbRequest.getQueryBuilder());
                break;

            default:
                break;
        }
        if (mDbRequest.getiListener() != null)
            sendToMainHandler(mDbRequest, result);
    }

    private <T> void sendToMainHandler(DBRequest<T> mDbRequest, Object obj) {
        mDbRequest.setResult(obj);
        final Message msg = new Message();
        msg.what = HANDLER_RESULT;
        msg.obj = mDbRequest;

        mHandler.post(new Runnable() {
            @Override public void run() {
                DBRequest mDbRequest = (DBRequest) msg.obj;
                mDbRequest.getiListener().onResult(mDbRequest.getResult());
                //销毁对象
                mDbRequest = null;
            }
        });
    }

    private class MRunable<T> implements Runnable {

        @Override
        public void run() {
            while (isRun) {
                mLock.lock();
                try {
                    if (dbRequests.size() <= 0) {
                        mCondition.await();
                    }

                    DBRequest<?> mDbRequest = null;
                    if (dbRequests.size() > 0) {
                        mDbRequest = dbRequests.remove(0);
                    }
                    mLock.unlock();
                    if (mDbRequest != null)
                        doAction(mDbRequest);

                } catch (InterruptedException e) {
                    mLock.unlock();
                }
            }
        }
    }

    //private class MHandler implements UiInstance.OnHandlerListener {
    //    @Override
    //    public void handleMessage(Message msg) {
    //        if (msg.what == HANDLER_RESULT) {
    //            DBRequest mDbRequest = (DBRequest) msg.obj;
    //            mDbRequest.getiListener().onResult(mDbRequest.getResult());
    //            //销毁对象
    //            mDbRequest = null;
    //        }
    //    }
    //
    //    @Override
    //    public void preMessage(Message msg) {
    //    }
    //}

}
